package game.block.entity;

import game.block.Locator;
import game.block.blocks.state.BlockState;
import game.block.blocks.state.JumpState;
import game.block.blocks.state.MoveState;
import game.block.blocks.state.NormalState;
import game.block.blocks.state.SpinState;
import game.block.command.JumpCommand;
import game.block.command.MoveCommand;
import game.block.game.Board;
import game.block.tile.Tile;
import game.block.turn.Turn;

import java.util.ArrayList;

import com.badlogic.gdx.math.Vector2;

public abstract class MovableEntity extends Entity {
    private ArrayList<Tile> adjacentTiles;
    private ArrayList<Tile> jumpTiles;
    private TYPE type;
    private boolean selected;
    private BlockState state;
    private boolean jumpEnabled;

    public MovableEntity(Vector2 pos, Tile tile, String path, TYPE t) {
        super(pos, tile, path);
        type = t;
        selected = false;
        state = new NormalState(this);
        jumpTiles = new ArrayList<Tile>();
        adjacentTiles = new ArrayList<Tile>();
    }

    public abstract boolean counterEffect(TrapEntity te);

    //mouse selected a tile when a block is selected
    public boolean select(Turn currentTurn, Tile t, Board board) {
        if (adjacentTiles.contains(t)) {
            currentTurn.addCommand(new MoveCommand(this, t, getCurrentTile()));
            selectTile(currentTurn, t, board);
            board.turnOver();
            return true;
        } else if (jumpTiles.contains(t)) {
            currentTurn.addCommand(new JumpCommand(this, t, getCurrentTile()));
            selectTile(currentTurn, t, board);
            board.turnOver();
            return true;
        }
        return false;
    }

    //apply traps effects for the tile that was selected
    public void selectTile(Turn currentTurn, Tile t, Board board) {
        unhighlightAdjacent();
        Locator.getAudio().playSound(SOUND.HOVER.getValue());
        t.changeColor(COLOR.MOVINGTO);
        for (Entity trap : t.getEntities()) {
            if (trap instanceof TrapEntity) {
                ((TrapEntity) trap).applyEffect(this, board, currentTurn, t);
            }
        }
    }

    public void start(Board board) {
        updateAvailableTiles(board);
    }

    public void updateAvailableTiles(Board board) {
        int cPos = (int) (currentTile.getPosition().x / TILE_SIZE);
        int rPos = (int) (currentTile.getPosition().y / TILE_SIZE);
        int arrayPos = (board.getCols() - 1 - rPos) * board.getRows() + cPos;

        board.unhighlightAllTiles();
        checkSurroundings(arrayPos, board);

        updateAdjacentTiles(board);

        updateJumpTiles(arrayPos, board);
        // add update explosion blocks in explode block class
    }

    public void checkSurroundings(int arrayPos, Board board) {
        this.setJumpEnabled(false);
        // check left tile
        if (arrayPos % board.getRows() != 0) {
            Tile tile = board.getTiles().get(arrayPos - 1);
            if (tile.containsJumpBlock()) {
                this.setJumpEnabled(true);
            }
        }
        // check right tile
        if (arrayPos % board.getRows() != board.getRows() - 1) {
            Tile tile = board.getTiles().get(arrayPos + 1);
            if (tile.containsJumpBlock()) {
                this.setJumpEnabled(true);
            }
        }
        // check top tile
        if (arrayPos - board.getRows() > 0) {
            Tile tile = board.getTiles().get(arrayPos - board.getRows());
            if (tile.containsJumpBlock()) {
                this.setJumpEnabled(true);
            }
        }
        // check bottom tile
        if (arrayPos + board.getRows() < board.getTiles().size()) {
            Tile tile = board.getTiles().get(arrayPos + board.getRows());
            if (tile.containsJumpBlock()) {
                this.setJumpEnabled(true);
            }
        }
    }

    public void updateJumpTiles(int arrayPos, Board board) {
        jumpTiles.clear();
        if (this.isJumpEnabled()) {
            // add left tiles
            if (arrayPos % board.getRows() != 1) {
                Tile tile = board.getTiles().get(arrayPos - 2);
                if (!tile.containsBlock() && !tile.containsWall()) {
                    jumpTiles.add(tile);
                }
            }
            // add right tiles
            if (arrayPos % board.getRows() != board.getRows() - 2) {
                Tile tile = board.getTiles().get(arrayPos + 2);
                if (!tile.containsBlock() && !tile.containsWall()) {
                    jumpTiles.add(tile);
                }
            }
            // add top tiles
            if (arrayPos - board.getRows() * 2 > 0) {
                Tile tile = board.getTiles().get(arrayPos - board.getRows() * 2);
                if (!tile.containsBlock() && !tile.containsWall()) {
                    jumpTiles.add(tile);
                }
            }
            // add bottom tiles
            if (arrayPos + board.getRows() * 2 < board.getTiles().size()) {
                Tile tile = board.getTiles().get(arrayPos + board.getRows() * 2);
                if (!tile.containsBlock() && !tile.containsWall()) {
                    jumpTiles.add(tile);
                }
            }
        }

    }

    public void updateAdjacentTiles(Board board) {
        adjacentTiles.clear();
        int cPos = (int) (currentTile.getPosition().x / TILE_SIZE);
        int rPos = (int) (currentTile.getPosition().y / TILE_SIZE);
        int arrayPos = (board.getCols() - 1 - rPos) * board.getRows() + cPos;
        // add left tile
        if (arrayPos % board.getRows() != 0) {
            Tile tile = board.getTiles().get(arrayPos - 1);
            if (!tile.containsBlock() && !tile.containsWall()) {
                adjacentTiles.add(tile);
            }
        }
        // add right tile
        if (arrayPos % board.getRows() != board.getRows() - 1) {
            Tile tile = board.getTiles().get(arrayPos + 1);
            if (!tile.containsBlock() && !tile.containsWall()) {
                adjacentTiles.add(tile);
            }
        }
        // add top tile
        if (arrayPos - board.getRows() > 0) {
            Tile tile = board.getTiles().get(arrayPos - board.getRows());
            if (!tile.containsBlock() && !tile.containsWall()) {
                adjacentTiles.add(tile);
            }
        }
        // add bottom tile
        if (arrayPos + board.getRows() < board.getTiles().size()) {
            Tile tile = board.getTiles().get(arrayPos + board.getRows());
            if (!tile.containsBlock() && !tile.containsWall()) {
                adjacentTiles.add(tile);
            }
        }

    }

    public void unhighlightAdjacent() {
        for (Tile tile : adjacentTiles) {
            tile.changeColor(COLOR.BLANK);
        }

        for (Tile tile : jumpTiles) {
            tile.changeColor(COLOR.BLANK);
        }
    }

    public void highlightAdjacent() {
        for (Tile tile : adjacentTiles) {
            tile.changeColor(COLOR.HOVER);
        }
        for (Tile tile : jumpTiles) {
            tile.changeColor(COLOR.HOVER);
        }
    }

    public void changeState(BlockState s) {
        state = s;
    }

    @Override
    public void update(float delta) {
        state.update(delta);
    }

    public void moveTo(Tile tile, float time) {
        state = new MoveState(this, tile, time);
    }

    public void jumpTo(Tile tile, float time) {
        state = new JumpState(this, tile, time);
    }

    public void spinTo(Tile tile, float time) {
        state = new SpinState(this, tile, time);

    }

    public void explode(Tile tile) {

    }

    public TYPE getType() {
        return type;
    }

    public void setType(TYPE type) {
        this.type = type;
    }

    public boolean isSelected() {
        return selected;
    }

    public void setSelected(boolean selected) {
        this.selected = selected;
    }

    public boolean isJumpEnabled() {
        return jumpEnabled;
    }

    public void setJumpEnabled(boolean jumpEnabled) {
        this.jumpEnabled = jumpEnabled;
    }

    public ArrayList<Tile> getAdjacentTiles() {
        return adjacentTiles;
    }

    public void setAdjacentTiles(ArrayList<Tile> adjacentTiles) {
        this.adjacentTiles = adjacentTiles;
    }

    public ArrayList<Tile> getJumpTiles() {
        return jumpTiles;
    }

    public void setJumpTiles(ArrayList<Tile> jumpTiles) {
        this.jumpTiles = jumpTiles;
    }

}
